﻿using System;
using System.IO;
using System.Windows.Forms;
using System.Windows.Markup;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using paravili.CaptureSources;
using Sebarf.Diagnostics.Interfaces;
using Capture = Emgu.CV.Capture;
using MemStorage = Emgu.CV.MemStorage;
using MotionHistory = Emgu.CV.MotionHistory;
using System.Collections.Generic;
using System.Windows.Shapes;
using System.Linq;
using System.Threading;
using paravili.Steps;
using System.Xml;
using Utils;
using System.Drawing;

namespace Tests.Basics.UI {
	public partial class MotionUI : Form {

		// TODO: Performance, Best matching...

		private string m_imagePath1 = "Sample_00194.jpg";
		private string m_imagePath2 = "Sample_00195.jpg";
		private string m_imagePath3 = "Sample_00197.jpg";
		private string m_imagePath4 = "Sample_00198.jpg";
		private string m_imagePath5 = "Sample_00200.jpg";
		private Image<Lab, Byte> m_image1;
		private Image<Lab, Byte> m_image2;
		private Image<Lab, Byte> m_image3;
		private Image<Lab, Byte> m_image4;
		private Image<Lab, Byte> m_image5;
		private ImageBox m_imageBox1;
		private ImageBox m_imageBox2;
		private ImageBox m_imageBox3;
		private ImageBox m_imageBox4;
		private ImageBox m_imageBox5;

		private ImageBox m_imageBox6;
		private ImageBox m_imageBox7;
		private ImageBox m_imageBox8;
		private ImageBox m_imageBox9;
		private Image<Lab, Byte> m_image7;
		private Image<Lab, Byte> m_image8;
		private Image<Lab, Byte> m_image9;
		private Image<Lab, Byte> m_image6;
		public MotionUI() {
			InitializeComponent();

			m_imageBox1 = new ImageBox();
			panel1.Controls.Add(m_imageBox1);
			m_imageBox2 = new ImageBox();
			panel2.Controls.Add(m_imageBox2);
			m_imageBox3 = new ImageBox();
			panel3.Controls.Add(m_imageBox3);
			m_imageBox4 = new ImageBox();
			panel4.Controls.Add(m_imageBox4);
			m_imageBox5 = new ImageBox();
			panel5.Controls.Add(m_imageBox5);
			m_imageBox1.Dock = DockStyle.Fill;
			m_imageBox2.Dock = DockStyle.Fill;
			m_imageBox3.Dock = DockStyle.Fill;
			m_imageBox4.Dock = DockStyle.Fill;
			m_imageBox5.Dock = DockStyle.Fill;

			m_imageBox6 = new ImageBox();
			panel6.Controls.Add(m_imageBox6);
			m_imageBox7 = new ImageBox();
			panel7.Controls.Add(m_imageBox7);
			m_imageBox8 = new ImageBox();
			panel8.Controls.Add(m_imageBox8);
			m_imageBox9 = new ImageBox();
			panel9.Controls.Add(m_imageBox9);
			m_imageBox6.Dock = DockStyle.Fill;
			m_imageBox7.Dock = DockStyle.Fill;
			m_imageBox8.Dock = DockStyle.Fill;
			m_imageBox9.Dock = DockStyle.Fill;

			LoadImages();
			CalculateDiff();
			var res1 = GroupCandidates(FindCanditates(m_image1, m_image2));
			var res2 = GroupCandidates(FindCanditates(m_image2, m_image3));
			var res3 = GroupCandidates(FindCanditates(m_image3, m_image4));
			var res4 = GroupCandidates(FindCanditates(m_image4, m_image5));

			FindMovement(res1, res2, m_image2, Color.Green);
			FindMovement(res2, res3, m_image3, Color.Orange);
			FindMovement(res3, res4, m_image4, Color.Red);

			m_imageBox6.Image = m_image2.PyrUp();
			m_imageBox7.Image = m_image3.PyrUp();
			m_imageBox8.Image = m_image4.PyrUp();
			//m_imageBox9.Image = m_image5.PyrUp();
			//	GroupCandidates(FindCanditates(m_image2, m_image3), m_image2);
			//	GroupCandidates(FindCanditates(m_image3, m_image4), m_image3);
			//	GroupCandidates(FindCanditates(m_image4, m_image5), m_image4);



			ICapture capture = null;
			try {
				capture = new Capture(); //create a camera captue
			}
			catch (Exception) {
				// Use stored images
				Logger.WriteInformation("No camera found");
				List<Image<Bgr, Byte>> images = new List<Image<Bgr, Byte>>();
				for (int i = 1; i <= 20; i++) {
					string fileName = @"..\..\..\..\..\..\099_Materials\Easy\MovingCircleWithLight\" + i.ToString("00000\\.jpg");
					//string fileName = @"Sample_" + i.ToString("00000\\.jpg");
					if (!System.IO.File.Exists(fileName)) {
						new TestVideoGraber().TestGrabVideoMaterialsMiddle();
					}
					images.Add(new Image<Bgr, Byte>(fileName).PyrDown());
				}
				capture = new ImageBasedCaptureDevice(images.ToArray());
				//eturn;
			}

			m_workerThread = new Thread(new ThreadStart(delegate() {
				string xamlFileName = "MotionByDiffColorsSteps.xaml";
				string fileName = Directory.GetCurrentDirectory() + "\\" + xamlFileName;
				Logger.WriteInformation("Loading:" + fileName);
				var dictionary = XamlReader.Load(XmlReader.Create(fileName)) as System.Windows.ResourceDictionary;
				Check.AssertException(dictionary.Contains("MotionByDiffColorsSteps"), "profile not exits");
				var profileConfig = dictionary["MotionByDiffColorsSteps"] as ProcessStep<ICapture>;

				NextImageProvider step1 = profileConfig as NextImageProvider;

				int index = 0;
				Image<Lab, Byte> last1image = null;
				List<List<ColoredPoint>> lastResult = null;

				while (m_isRunning) {
					index++;
					var canditates = step1.Process(capture) as IDictionary<Point, Lab>;// .Convert<Lab, byte>();

					if (canditates != null) {
						var currentImage = step1.CurrentImage;
						//var candidates = FindCanditates(currentImage, last1image);
						if (canditates.Count == 0) {
							this.Invoke(new ThreadStart(delegate() { m_imageBox9.Image = currentImage; }));
							continue;
						}
						var currentResult = GroupCandidates(canditates);

						if (lastResult == null) {
							lastResult = currentResult;

						}
						else {

							var res10 = lastResult;
							var toDisplay = currentImage.Clone();

							FindMovement(currentResult, res10, toDisplay, Color.Green);
							if (this.checkBox1.CheckState == CheckState.Checked) {

								toDisplay.Save(index.ToString("0000\\.jpg"));
							}
							this.Invoke(new ThreadStart(delegate() { m_imageBox9.Image = toDisplay; }));

							lastResult = currentResult;
						}
					}
					//last1image = currentImage;
					Thread.Sleep(10);
				}
				this.Invoke(new ThreadStart(delegate() { this.Close(); }));

			}));
			m_workerThread.Start();

		}
		bool m_isRunning = true;
		protected override void OnClosing(System.ComponentModel.CancelEventArgs e) {
			if (m_isRunning == true) {
				e.Cancel = true;
			}
			m_isRunning = false;
		}

		private Thread m_workerThread;
		private void FindMovement(ICollection<List<ColoredPoint>> diff1, List<List<ColoredPoint>> diff2, Image<Lab, Byte> result, Color color) {
			if (diff1.Count < 1 || diff2.Count < 1) {
				return;
			}
			var motionMembory = vScrollBar4.Value;
			var maxDistance = vScrollBar2.Value;
			var diff1Points = (from s in diff1
							   orderby s.Average(c => c.P.X), s.Average(c => c.P.Y)
							   select
								(new {
									Original = s,
									X = s.Average(c => c.P.X),
									Y = s.Average(c => c.P.Y),
									GreenColor = s.Average(c => c.Color.Value),
									RedColor = s.Average(c => c.Color.Satuation),
									BlueColor = s.Average(c => c.Color.Hue)
								})).ToArray();
			var diff2Points = (from s in diff2
							   orderby s.Average(c => c.P.X), s.Average(c => c.P.Y)
							   select
								(new {
									Original = s,
									X = s.Average(c => c.P.X),
									Y = s.Average(c => c.P.Y),
									GreenColor = s.Average(c => c.Color.Value),
									RedColor = s.Average(c => c.Color.Satuation),
									BlueColor = s.Average(c => c.Color.Hue)
								})).ToArray();
			List<KeyValuePair<Point, Point>> motionVectors = new List<KeyValuePair<Point, Point>>();
			foreach (var entry in diff2Points) {
				var tmp = (from s in diff1Points
						   select
							new {
								P = s,
								diff =
						Math.Pow(Math.Pow(Math.Pow(s.X - entry.X, 2) + Math.Pow(s.Y - entry.Y, 2), 0.5), 2) +
						(Math.Abs(entry.GreenColor - s.GreenColor)) + (Math.Abs(entry.BlueColor - s.BlueColor)) +
						(Math.Abs(entry.RedColor - s.RedColor))
							});
				var target = tmp.OrderBy(t => t.diff).First();
				if (target.diff > maxDistance) {
					continue;
				}
				//		var last = tmp.OrderBy(t => t.diff).Last();

				motionVectors.Add(new KeyValuePair<Point, Point>(new Point((int)entry.X, (int)entry.Y),
																   new Point((int)target.P.X, (int)target.P.Y)));

				Point[] points = entry.Original.Select(y => y.P).ToArray();
				Point[] targetPoints = target.P.Original.Select(y => y.P).ToArray();

				//	result.DrawPolyline(targetPoints, true, new Lab(50, 1000, 500), 1);
				//	result.DrawPolyline(points, true, new Lab(50, 1000, 5000), 1);
				result.Draw(new CircleF(new Point((int)entry.X, (int)entry.Y), 1), new Lab(50, 1000, 500), 1);


			}

			m_motionVectors.Enqueue(motionVectors);
			while (m_motionVectors.Count > (motionMembory + 1)) {
				m_motionVectors.Dequeue();
			}
			int colorValue = 1000;
			foreach (var vectors in m_motionVectors) {
				foreach (var vector in vectors) {
					result.DrawPolyline(new Point[] { vector.Key, vector.Value }, false, new Lab(50, 1000, 500), 5);
				}
				colorValue += 500;
			}
		}

		private Queue<List<KeyValuePair<Point, Point>>> m_motionVectors = new Queue<List<KeyValuePair<Point, Point>>>();

		private Dictionary<Point, Lab> FindCanditates(Image<Lab, Byte> first, Image<Lab, Byte> second) {
			Dictionary<Point, Lab> candidates = new Dictionary<Point, Lab>();
			var diff = (first - second);
			int threshhold = vScrollBar1.Value;
			for (int col = 0; col < diff.Cols; col++) {
				for (int row = 0; row < diff.Rows; row++) {
					var totalIntensity = diff[row, col].Value;
					if (totalIntensity > threshhold) {
						candidates.Add(new Point(col, row), diff[row, col]);

					}
				}
			}

			return candidates;
		}
		private class ColoredPoint {
			public Point P { get; set; }
			public Lab Color { get; set; }
		}

		private void findNeigbours(ColoredPoint p, IDictionary<Point, Lab> candidates, List<ColoredPoint> group) {

			int clusterSearchingSensivity = vScrollBar5.Value;
			List<ColoredPoint> pointsToProcess = new List<ColoredPoint>();
			List<ColoredPoint> pointsProcessed = new List<ColoredPoint>();

			pointsToProcess.Add((p));
			candidates.Remove(p.P);
			int cnt0 = 0;
			do {
				List<ColoredPoint> pointsToRemove = new List<ColoredPoint>();
				List<ColoredPoint> pointsToAdd = new List<ColoredPoint>();

				foreach (var o in pointsToProcess) {
					for (int i = -clusterSearchingSensivity; i <= clusterSearchingSensivity; i++) {
						for (int y = -clusterSearchingSensivity; y <= clusterSearchingSensivity; y++) {
							if (y == 0 && i == 0) {
								continue;
							}
							cnt0++;
							var newPoint = o.P + new Size(i, y);
							if (candidates.ContainsKey(newPoint)) {
								var cp = new ColoredPoint() { Color = candidates[newPoint], P = newPoint };
								pointsToAdd.Add(cp);
								candidates.Remove(newPoint);
								group.Add(cp);

							}
						}
					}
					pointsToRemove.Add(o);
				}
				foreach (var o in pointsToAdd)
					pointsToProcess.Add(o);
				foreach (var o in pointsToRemove)
					pointsToProcess.Remove(o);
			} while (pointsToProcess.Count > 0);
			Logger.WriteDebug(cnt0 + "");

		}

		private List<List<ColoredPoint>> GroupCandidates(IDictionary<Point, Lab> candidates) {
			List<List<ColoredPoint>> figures = new List<List<ColoredPoint>>();
			var maxCluserSize = vScrollBar3.Value;
			int groupCandidatesFound = 0;
			while (candidates.Count > 0) {
				var entry = candidates.First();
				var group = new List<ColoredPoint>();
				findNeigbours(new ColoredPoint() { Color = entry.Value, P = entry.Key }, candidates, group);
				if (group.Count > 1 && group.Count < maxCluserSize) {
					figures.Add(group);
				}
				groupCandidatesFound++;

			}
			Logger.WriteDebug("groupCandidatesFound:" + groupCandidatesFound);
			return figures;
		}

		private void CalculateDiff() {
			m_image6 = (m_image1 - m_image2).PyrUp();
			m_imageBox6.Image = m_image6;
			m_image7 = (m_image2 - m_image3).PyrUp();
			m_imageBox7.Image = m_image7;
			m_image8 = (m_image3 - m_image4).PyrUp();
			m_imageBox8.Image = m_image8;
			m_image9 = (m_image4 - m_image5).PyrUp();
			m_imageBox9.Image = m_image9;
		}

		private void LoadImages() {
			m_image1 = new Image<Lab, Byte>(m_imagePath1).PyrDown().PyrDown();
			m_imageBox1.Image = m_image1;
			m_image2 = new Image<Lab, Byte>(m_imagePath2).PyrDown().PyrDown();
			m_imageBox2.Image = m_image2;
			m_image3 = new Image<Lab, Byte>(m_imagePath3).PyrDown().PyrDown();
			m_imageBox3.Image = m_image3;
			m_image4 = new Image<Lab, Byte>(m_imagePath4).PyrDown().PyrDown();
			m_imageBox4.Image = m_image4;
			m_image5 = new Image<Lab, Byte>(m_imagePath5).PyrDown().PyrDown();
			m_imageBox5.Image = m_image5;

		}

		private void label2_Click(object sender, EventArgs e) {

		}
		// Points in the plane

	}
}
